// Net.cpp

#include "stdafx.h"

#include <fstream>
#include "Net.h"
#include "PlayerMgr.h"

Net::Peer::Peer()
{
	m_bStateUpdated=false;
	m_iLastMessageTime = 0;
};

Net::Peer::~Peer()
{
	int i,iC=m_apxQueueIn.GetSize();
	for(i=0;i<iC;i++)
	{
		delete m_apxQueueIn[i];
	};
	m_apxQueueIn.Clear();
	iC=m_apxQueueOut.GetSize();
	for(i=0;i<iC;i++)
	{
		delete m_apxQueueOut[i];
	};
	m_apxQueueOut.Clear();
};

void Net::Peer::Swap(Peer& p_xP)
{
	::Swap(m_xAddr,p_xP.m_xAddr);
	m_apxQueueIn.Swap(p_xP.m_apxQueueIn);
	m_apxQueueOut.Swap(p_xP.m_apxQueueOut);
	::Swap(m_xState,p_xP.m_xState);
	::Swap(m_bStateUpdated,p_xP.m_bStateUpdated);
};


Net::Net()
{
	m_aiBuffer=new byte[BufferSize];
	m_hasInit = false;
	m_bInGame=false;
};

Net::~Net()
{
	Shut();
	delete [] m_aiBuffer;
};

int Net::SizeOfMsg(byte p_iType)
{
	switch(p_iType)
	{
	case MT_Ping: return sizeof(Msg); break;
	case MT_Pong: return sizeof(Msg); break;
	case MT_Quit: return sizeof(Msg); break;
	case MT_WorldState:return sizeof(MsgWorldState);break;
	default:return -1;break;
	};
	return -1;
};

Net::Msg* Net::AllocMsg(byte p_yType)
{
	int iSize=SizeOfMsg(p_yType);
	if(iSize<=0){return NULL;};
	Msg* pxM=(Msg*)(malloc(iSize));
	pxM->m_iType=p_yType;
	return pxM;
};

void Net::FreeMsg(Msg* p_pxMsg)
{
	free(p_pxMsg);
};

dword Net::Init()
{
	Shut();
	m_bInGame=false;
	IPAddr xLocal=IPAddr::GetLocalDefault();
	xLocal.SetPort(GamePort);
	if(!m_xSock.Open(xLocal))
	{
		printf("Can't open socket at %s\n",xLocal.ToStr().c_str());
		return 0;
	}
	else
	{
		printf("Network ready at %s\n",xLocal.ToStr().c_str());
	};
	m_hasInit = true;
	m_bInGame = true;
	m_iScanCounter=-1;
	m_xLocalAddress=xLocal;
	//Add my self to the peer list
	Peer& xMe = m_axPeers.NewRef();
	xMe.m_xState.m_axPlayers[0].m_dwIPID = xLocal.GetHost();
	xMe.m_xAddr = xLocal;
	return xLocal.GetHost();
};

void Net::Shut()
{
	if(!m_xSock.IsOpen()){return;};
	m_xSock.Close();
	m_hasInit = false;
	printf("Network closed.\n");
};

bool Net::IsRunning() const
{
	return m_xSock.IsOpen();
};

int Net::ScanNetwork()
{
	int iFoundPeers = 0;
	IPAddr xRemote = m_xLocalAddress;
	while (m_iScanCounter < 255)
	{
		m_iScanCounter++;
		xRemote.sin_addr.S_un.S_un_b.s_b4 = m_iScanCounter;

		Msg *pxPing = AllocMsg(MT_Ping);

		if (m_xSock.SendTo(xRemote, pxPing, sizeof(Msg)) <= 0)
		{
			printf("Couldn't send ping request to %s\n", xRemote.ToStr().c_str());
		}
		else
		{
			//printf("Ping sent to: %s\n", xRemote.ToStr().c_str());
		};

	};

	m_iScanCounter = 1;

	//Do some external calls here
	//----

	Peer* xImportedRemotes = new Peer[10];
	int iSize = ImportRemotes(xImportedRemotes);

	if ((iSize != -1) || (iSize==0))
	{
		Msg* pxPing = (Msg*)AllocMsg(MT_Ping);
		pxPing->m_iType = MT_Ping;

		for (int t = 0; t < iSize; t++)
		{
			if (m_xSock.SendTo(xImportedRemotes[t].m_xAddr, pxPing, sizeof(Msg)) <= 0)
			{
				printf("Couldn't send ping request to %s\n", xImportedRemotes[t].m_xAddr.ToStr().c_str());
			}
			else
			{
				printf("Ping sent to: %s\n", xImportedRemotes[t].m_xAddr.ToStr().c_str());
			};
		};
	}
	
	delete [] xImportedRemotes;
	xImportedRemotes = NULL;
	//----

	return 0;
};

void Net::BroadcastLocalState()
{
	int iMeIdx=FindPeer(m_xLocalAddress.GetHost());
	if(iMeIdx==-1){return;};
	int i,iC=m_axPeers.GetSize();
	for(i=0;i<iC;i++)
	{
		if(i!=iMeIdx)
		{
			MsgWorldState* pxWrld=(MsgWorldState*)AllocMsg(MT_WorldState);
			//Sender timestamp? this will be a unique ID
			pxWrld->m_yPacketID = ::GetTickCount();
			pxWrld->m_yNumPlayers = m_axPeers.GetSize();
			SortPeers();
			FillWorldStateMsg(m_axPeers[iMeIdx].m_xState,pxWrld);
			m_axPeers[i].m_apxQueueOut.Add(pxWrld);
		};
	};

	iC = m_axKnownHosts.GetSize();
	for (i = 0; i < iC; i++)
	{
		MsgWorldState* pxWrld=(MsgWorldState*)AllocMsg(MT_WorldState);
		pxWrld->m_iType = MT_WorldState;
		//Sender timestamp? this will be a unique ID
		pxWrld->m_yPacketID = ::GetTickCount();
		pxWrld->m_yNumPlayers = m_axPeers.GetSize();
		SortPeers();
		FillWorldStateMsg(m_axPeers[iMeIdx].m_xState,pxWrld);
		int count = 0;
		count = m_xSock.SendTo(m_axKnownHosts[i], pxWrld, sizeof(MsgWorldState));
		printf("%d bytes sent to via knownhost: %s\n", count, m_axKnownHosts[i].ToStr().c_str());
	
	};
};

void Net::FillWorldStateMsg(const WorldState& p_xW,MsgWorldState* p_msg)
{
	int i;
	for(i=0;i<m_axPeers.GetSize();i++)
	{
		const PlayerState& xP=p_xW.m_axPlayers[i];

		p_msg->m_axPlayerStates[i].m_dwIPID = xP.m_dwIPID; 
		p_msg->m_axPlayerStates[i].m_fRotation = xP.m_fRotation;
		p_msg->m_axPlayerStates[i].m_fVelocityX = xP.m_fVelocityX;
		p_msg->m_axPlayerStates[i].m_fVelocityY = xP.m_fVelocityY;
		p_msg->m_axPlayerStates[i].m_fAccelerationX = xP.m_fAccelerationX;
		p_msg->m_axPlayerStates[i].m_fAccelerationY = xP.m_fAccelerationY;
		p_msg->m_axPlayerStates[i].m_fThrust = xP.m_fThrust;
		p_msg->m_axPlayerStates[i].m_fSteering = xP.m_fSteering;
		p_msg->m_axPlayerStates[i].m_dwScore = xP.m_dwScore;
		p_msg->m_axPlayerStates[i].m_fGemPosX = xP.m_fGemPosX;
		p_msg->m_axPlayerStates[i].m_fGemPosY = xP.m_fGemPosY;
		p_msg->m_axPlayerStates[i].m_dwGemID = xP.m_dwGemID;
	};
};

void Net::Think()
{
	if(!m_xSock.IsOpen()){return;};
	
	{
		int iPeerIdx,iPeerCnt=m_axPeers.GetSize();
		for(iPeerIdx=0;iPeerIdx<iPeerCnt;iPeerIdx++)
		{
			Peer& xP=m_axPeers[iPeerIdx];
			if(xP.m_apxQueueOut.GetSize()>0)
			{
				int iPktSize=0;
				bool bStop=false;
				while(
					(iPktSize<MaxPacketSize)&&
					(xP.m_apxQueueOut.GetSize()>0)&&
					(!bStop)
					)
				{
					Msg* pxM=xP.m_apxQueueOut[0];
					int iMsgSize=SizeOfMsg(pxM->m_iType);
					if(iMsgSize<=0)
					{
						break;
					};
					if(iMsgSize+iPktSize<=MaxPacketSize)
					{
						xP.m_apxQueueOut.Delete(0);
						memcpy(m_aiBuffer+iPktSize,pxM,iMsgSize);
						iPktSize+=iMsgSize;
						FreeMsg(pxM);
					}
					else
					{
						bStop=true;
					};
				};
				if(iPktSize>0)
				{
					m_xSock.SendTo(xP.m_xAddr,m_aiBuffer,iPktSize);
					printf("%d bytes sent to %s\n", iPktSize, xP.m_xAddr.ToStr().c_str());
				};
			};
		};
	};
	while(m_xSock.PendingRecv()>0)
	{
		IPAddr xFrom;
		int iPktSize=m_xSock.RecvFrom(xFrom,m_aiBuffer,BufferSize);
		if((iPktSize>0)&&(m_xLocalAddress.GetHost()!=xFrom.GetHost()))
		{
			if(m_aiBuffer[0]==MT_Ping)
			{
				Msg* pxPong = (Msg*)AllocMsg(MT_Pong);
				m_xSock.SendTo(xFrom, pxPong, sizeof(Msg));
			}
			else if(m_aiBuffer[0]==MT_Pong)
			{
				dword dwPeerID=xFrom.GetHost();
				int iPeerIdx=FindPeer(dwPeerID);
				if((iPeerIdx==-1) && (m_axPeers.GetSize() < WorldState::MaxPlayers))
				{
					//Add to known host list
					m_axKnownHosts.Add(xFrom);
					printf("New user at: %s", xFrom.ToStr().c_str());
				}
			}
			else if(m_aiBuffer[0]==MT_Quit)
			{
				dword dwPeerID=xFrom.GetHost();
				int iPeerIdx=FindPeer(dwPeerID);
				if(iPeerIdx!=-1)
				{
					Msg* pxMsg=AllocMsg(MT_Quit);
					memcpy(pxMsg,m_aiBuffer,1);
					m_axPeers[iPeerIdx].m_apxQueueIn.Add(pxMsg);
				};
				
			}
			else
			{
				dword dwPeerID=xFrom.GetHost();
				int iPeerIdx=FindPeer(dwPeerID);
				if(iPeerIdx!=-1)
				{
					int iMsgStart=0;
					while(iMsgStart<iPktSize)
					{
						byte yMsgType=m_aiBuffer[iMsgStart];
						int iMsgSize=SizeOfMsg(yMsgType);

						if(iMsgSize<0)
						{
							iMsgStart=iPktSize;
						}
						else
						{
							Msg* pxMsg=AllocMsg(yMsgType);
							memcpy(pxMsg,m_aiBuffer+iMsgStart,iMsgSize);
							m_axPeers[iPeerIdx].m_apxQueueIn.Add(pxMsg);
							iMsgStart+=iMsgSize;
						};
					};
				}
				else
				{
					if (m_axPeers.GetSize() < WorldState::MaxPlayers)
					{
						Peer& newPeer = m_axPeers.NewRef();
						newPeer.m_xAddr = xFrom;
						int iMsgStart=0;
						while(iMsgStart<iPktSize)
						{
							byte yMsgType=m_aiBuffer[iMsgStart];
							int iMsgSize=SizeOfMsg(yMsgType);

							if(iMsgSize<0)
							{
								iMsgStart=iPktSize;
							}
							else
							{
								Msg* pxMsg=AllocMsg(yMsgType);
								memcpy(pxMsg,m_aiBuffer+iMsgStart,iMsgSize);
								newPeer.m_apxQueueIn.Add(pxMsg);
								iMsgStart+=iMsgSize;
							};
						};

						int t = m_axKnownHosts.Find(xFrom);
						m_axKnownHosts.Delete(t);
					}
				};
			};
		};
	};
	int iPeerIdx;
	for(iPeerIdx=0;iPeerIdx<m_axPeers.GetSize();iPeerIdx++)
	{
		IPAddr xAddr=m_axPeers[iPeerIdx].m_xAddr;
		while(m_axPeers[iPeerIdx].m_apxQueueIn.GetSize()>0)
		{
			Msg* pxM=m_axPeers[iPeerIdx].m_apxQueueIn[0];
			m_axPeers[iPeerIdx].m_apxQueueIn.Delete(0);
			HandleMsg(xAddr,pxM);
			FreeMsg(pxM);
		};
	};
};

int Net::FindPeer(dword p_dwID)
{
	int i,iC=m_axPeers.GetSize();
	for(i=0;i<iC;i++)
	{
		if(m_axPeers[i].m_xAddr.GetHost()==p_dwID)
		{
			return i;
		};
	};
	return -1;
};

void Net::DumpPeers()
{
	int i,iC=m_axPeers.GetSize();
	for(i=0;i<iC;i++)
	{
		printf("%2d: %s\n",i+1,m_axPeers[i].m_xAddr.ToStr().c_str());
	};
	printf("%d peers\n",iC);
};

void Net::SortPeers()
{
	bool bDone=false;
	int i,iC=m_axPeers.GetSize();
	while(!bDone)
	{
		bDone=true;
		for(i=0;i<iC-1;i++)
		{
			Peer& xA=m_axPeers[i];
			Peer& xB=m_axPeers[i+1];
			if((dword)xA.m_xAddr.GetHost()>(dword)xB.m_xAddr.GetHost())
			{
				bDone=false;
				xA.Swap(xB);
			};
		};
	};
};

void Net::HandleMsg(const IPAddr& p_xAddr,Msg* p_pxMsg)
{
	int iMsgType=p_pxMsg->m_iType;
	
	if(iMsgType==MT_Ping)
	{
		if (m_axPeers.GetSize() < WorldState::MaxPlayers)
		{
			int idx = FindPeer(p_xAddr.GetHost());
			if (idx != -1)
			{
				Peer& newPlayer = m_axPeers[idx];

				Msg* pxEcho = (Msg*)AllocMsg(MT_Pong);
				pxEcho->m_iType = MT_Pong;

				newPlayer.m_apxQueueOut.Add(pxEcho);

				printf("I've been pinged by: %s\n", p_xAddr.ToStr().c_str());
			};
		};
	}
	else if(iMsgType==MT_Pong)
	{
		printf("I recieved a pong from: %s\n", p_xAddr.ToStr().c_str());
	}
	else if (iMsgType==MT_Quit)
	{
		int idx = FindPeer(p_xAddr.GetHost());
		if (idx != -1)
		{
			//Maybe we should delete from the player state array to
			m_axPeers.Delete(idx);
		};
	}
	else if(iMsgType==MT_WorldState)
	{
		MsgWorldState* pxWrld=(MsgWorldState*)p_pxMsg;
		int iIdx=FindPeer(p_xAddr.GetHost());
		if(iIdx!=-1)
		{
			//The player was found, update his world state.
			Peer& xFromPeer=m_axPeers[iIdx];
			xFromPeer.m_iLastMessageTime = GetTickCount();
			int i;
			int iC = pxWrld->m_yNumPlayers;
			for(i=0;i<iC;i++)
			{
				PlayerState& xP=xFromPeer.m_xState.m_axPlayers[i];
				xP.m_dwIPID = pxWrld->m_axPlayerStates[i].m_dwIPID; 
				xP.m_fRotation = pxWrld->m_axPlayerStates[i].m_fRotation;
				xP.m_fVelocityX = pxWrld->m_axPlayerStates[i].m_fVelocityX;
				xP.m_fVelocityY = pxWrld->m_axPlayerStates[i].m_fVelocityY;
				xP.m_fAccelerationX = pxWrld->m_axPlayerStates[i].m_fAccelerationX;
				xP.m_fAccelerationY = pxWrld->m_axPlayerStates[i].m_fAccelerationY;
				xP.m_fThrust = pxWrld->m_axPlayerStates[i].m_fThrust;
				xP.m_fSteering = pxWrld->m_axPlayerStates[i].m_fSteering;
				xP.m_dwScore = pxWrld->m_axPlayerStates[i].m_dwScore;
				xP.m_fGemPosX = pxWrld->m_axPlayerStates[i].m_fGemPosX;
				xP.m_fGemPosY = pxWrld->m_axPlayerStates[i].m_fGemPosY;
				xP.m_dwGemID = pxWrld->m_axPlayerStates[i].m_dwGemID;
			
			};
			xFromPeer.m_bStateUpdated=true;

			printf("Worldstate msg reccieved from: %s\n", p_xAddr.ToStr().c_str());
		}
		else
		{
			//The player was not found, add him to the peer list
			if (m_axPeers.GetSize() < WorldState::MaxPlayers)
			{
				Peer& newPeer = m_axPeers.NewRef();
				newPeer.m_xAddr = p_xAddr;
				newPeer.m_iLastMessageTime = ::GetTickCount();
				int i, iC = pxWrld->m_yNumPlayers;
				for(i=0;i<iC;i++)
				{
					PlayerState& xP=newPeer.m_xState.m_axPlayers[i];
					xP.m_dwIPID = pxWrld->m_axPlayerStates[i].m_dwIPID; 
					xP.m_fRotation = pxWrld->m_axPlayerStates[i].m_fRotation;
					xP.m_fVelocityX = pxWrld->m_axPlayerStates[i].m_fVelocityX;
					xP.m_fVelocityY = pxWrld->m_axPlayerStates[i].m_fVelocityY;
					xP.m_fAccelerationX = pxWrld->m_axPlayerStates[i].m_fAccelerationX;
					xP.m_fAccelerationY = pxWrld->m_axPlayerStates[i].m_fAccelerationY;
					xP.m_fThrust = pxWrld->m_axPlayerStates[i].m_fThrust;
					xP.m_fSteering = pxWrld->m_axPlayerStates[i].m_fSteering;
					xP.m_dwScore = pxWrld->m_axPlayerStates[i].m_dwScore;
					xP.m_fGemPosX = pxWrld->m_axPlayerStates[i].m_fGemPosX;
					xP.m_fGemPosY = pxWrld->m_axPlayerStates[i].m_fGemPosY;
					xP.m_dwGemID = pxWrld->m_axPlayerStates[i].m_dwGemID;
				}
			}
			printf("Worldstate msg reccieved from: %s\n", p_xAddr.ToStr().c_str());
		}

	}
};

int Net::GetMajorityScore(int p_iPeerIdx)
{
	int i,j,iC=m_axPeers.GetSize();
	if(p_iPeerIdx>=iC){return 0;};
	int iBestScore=0;
	int iBestVotes=0;
	for(i=0;i<iC;i++)
	{
		int iCurScore=m_axPeers[i].m_xState.m_axPlayers[p_iPeerIdx].m_dwScore;
		int iCurVotes=0;
		for(j=0;j<iC;j++)
		{
			int iScore=m_axPeers[j].m_xState.m_axPlayers[p_iPeerIdx].m_dwScore;
			if((iCurScore==iScore)&&(iScore!=-1))
			{
				iCurVotes++;
			};
		};
		if(iCurVotes>iBestVotes)
		{
			iBestVotes=iCurVotes;
			iBestScore=iCurScore;
		};
	};
	return iBestScore;
};

int Net::ImportRemotes(Peer* p_remotes)
{
	std::ifstream stream;
	stream.open("adresses.txt");
	if (!stream.is_open())
	{
		printf("Couldn't open the adress text file\n");
		return -1;
	};

	int counter = 0;
	while (!stream.eof())
	{
		char* temp = new char[20];
		stream.getline(temp, 20);
		counter++;
	};

	stream.close();
	stream.open("adresses.txt");
	stream.clear();

	for (int i = 0; i < counter; i++)
	{
		char* temp = new char[20];
		stream.getline(temp, 20);

		IPAddr newRemote(const_cast<const char*>(temp));
		p_remotes[i].m_xAddr = newRemote;
	}

	stream.close();

	return counter;
};